﻿using Abp.Dependency;
using Abp.Domain.Repositories;
using Castle.Core.Logging;
using System;
using System.Collections.Generic;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.Linq;
using Abp.Linq;
using Abp.Domain.Services;

namespace DevM.Development.Resources
{
    public class ResourceManager : ITransientDependency, IDomainService
    {

        /// <summary>
        /// 跟目录
        /// </summary>
        private static string RootDir = Path.Combine(Environment.CurrentDirectory, "DevM/Resources");
        private readonly IRepository<Resource, long> resouceRepository;
        private readonly IRepository<RecordResource, long> recordResouceRepository;
        private readonly ILogger logger;

        public ResourceManager(IRepository<Resource, long> resouceRepository, IRepository<RecordResource, long> recordResouceRepository, ILogger logger)
        {
            this.resouceRepository = resouceRepository;
            this.recordResouceRepository = recordResouceRepository;
            this.logger = logger;
        }

        public IQueryable<RecordResource> RecordResources { get => recordResouceRepository.GetAll(); }

        /// <summary>
        /// 指定资源根目录
        /// </summary>
        /// <param name="dir"></param>
        public async Task SetRootDirAsync(string dir)
        {
            if (string.IsNullOrEmpty(RootDir)) throw new Exception("资源根目录不能为空！");
            if (!Directory.Exists(dir)) throw new Exception("指定目录不存在！");
            RootDir = dir;
        }

        /// <summary>
        /// 获取文件md5
        /// </summary>
        /// <param name="stream"></param>
        /// <returns></returns>
        private static async Task<string> GetMD5(Stream stream)
        {
            try
            {
                stream.Position = 0;
                MD5 md5 = new MD5CryptoServiceProvider();
                byte[] retval = md5.ComputeHash(stream);

                StringBuilder sc = new StringBuilder();
                for (int i = 0; i < retval.Length; i++)
                {
                    sc.Append(retval[i].ToString("x2"));
                }
                return sc.ToString();
            }
            catch (Exception ex)
            {
                //
            }
            return null;
        }
        /// <summary>
        /// 保存资源
        /// </summary>
        /// <param name="resourceAddInfo"></param>
        /// <returns></returns>
        public async Task<Resource> AddResource(ResourceAddInfo resourceAddInfo)
        {
            var res = new Resource
            {
                Name = resourceAddInfo.Name,
                Type = resourceAddInfo.Type,
                Size = resourceAddInfo.Content.Length,
                TenantId = resourceAddInfo.TenantId,
                CreationTime = DateTime.Now,
                CreatorUserId = resourceAddInfo.CreateUserId,
                Path = $"{(resourceAddInfo.TenantId == null ? 1 : resourceAddInfo.TenantId)}/{resourceAddInfo.CreateUserId}/{await GetMD5(resourceAddInfo.Content)}{Path.GetExtension(resourceAddInfo.Name)}"
            };

            var savePath = Path.Combine(RootDir, res.Path);
            if (!File.Exists(savePath))
            {
                resourceAddInfo.Content.Position = 0;
                if (!Directory.Exists(Path.GetDirectoryName(savePath)))
                {
                    Directory.CreateDirectory(Path.GetDirectoryName(savePath));
                }
                using (var file = File.Open(savePath, FileMode.Create))
                {
                    var buffer = new byte[res.Size];
                    resourceAddInfo.Content.Read(buffer, 0, buffer.Length);
                    file.Write(buffer, 0, buffer.Length);
                    file.Flush();
                }
            }
            var id= await resouceRepository.InsertAndGetIdAsync(res);
            return await resouceRepository.GetAsync(id);
        }

        /// <summary>
        /// 删除资源
        /// </summary>
        /// <param name="resourceId"></param>
        public async Task DeleteResource(params long[] resourceId)
        {

            foreach (var resource in await GetResourceListById(resourceId))
            {
                resouceRepository.Delete(resource);
                var savePath = Path.Combine(RootDir, resource.Path);
                if (File.Exists(savePath) && resouceRepository.GetAll().Where(r => r.Path == resource.Path).Count() == 0)
                {
                    lock ("DeleteResource")
                    {
                        if (File.Exists(savePath) && resouceRepository.GetAll().Where(r => r.Path == resource.Path).Count() == 0)
                        {
                            try
                            {
                                File.Delete(savePath);
                            }
                            catch (Exception ex)
                            {
                                logger.Error("文件删除失败：", ex);
                            }
                        }
                    }
                }
            }
        }
        /// <summary>
        /// 根据id集获取资源列表
        /// </summary>
        /// <param name="resourceId"></param>
        /// <returns></returns>
        public async Task<List<Resource>> GetResourceListById(params long[] resourceId)
        {
            var result = new List<Resource>();
            foreach (var id in resourceId)
            {
                try
                {
                    var res = await resouceRepository.GetAsync(id);
                    result.Add(res);
                }
                catch (Exception)
                {
                    //
                }
            }
            return result;
        }
        /// <summary>
        /// 根据id获取资源信息（下载）
        /// </summary>
        /// <param name="resourceId"></param>
        /// <returns></returns>
        public async Task<ResourceGetInfo> GetResourceById(long resourceId)
        {
            var res = resouceRepository.Get(resourceId);
            var resourceInfo = new ResourceGetInfo();
            resourceInfo.Name = res.Name;
            resourceInfo.Type = res.Type;
            var savePath = Path.Combine(RootDir, res.Path);
            if (File.Exists(savePath))
            {
                using (var file = File.OpenRead(savePath))
                {
                    var buffer = new byte[res.Size];
                    file.Read(buffer, 0, buffer.Length);
                    resourceInfo.Content = buffer;
                }
            }
            return resourceInfo;
        }

        /// <summary>
        /// 添加资料
        /// </summary>
        /// <param name="environment"></param>
        /// <returns></returns>
        public async Task<List<RecordResource>> AddRecordResource(List<RecordResource> projectResources)
        {
            var res = new List<RecordResource>();
            foreach (var projectResource in projectResources)
            {
                projectResource.CreationTime = DateTime.Now;
                res.Add(await recordResouceRepository.InsertAsync(projectResource));
            }
            return res;
        }
        /// <summary>
        /// 删除资料
        /// </summary>
        /// <param name="deleterUserId"></param>
        /// <param name="environmentId"></param>
        /// <returns></returns>
        public async Task DeleteRecordResource(string recordType,params long[] recordResourceId)
        {
            var prs = await recordResouceRepository.GetAllListAsync(rr =>rr.RecoordType== recordType&& recordResourceId.Contains(rr.Id));
            await recordResouceRepository.DeleteAsync(rr => rr.RecoordType == recordType && recordResourceId.Contains(rr.Id));
            await DeleteResource(prs.Select(p => p.RecoordId).ToArray());
        }
        /// <summary>
        /// 根据项目获取资料
        /// </summary>
        /// <param name="projectId"></param>
        /// <returns></returns>
        public async Task<List<RecordResource>> GetRecordResources(string recordType, long recordId)
        {
            return recordResouceRepository.GetAll().Where(en =>en.RecoordType== recordType&&recordId==en.RecoordId).OrderBy(en=>en.Id).ToList();
        }

    }
}
